﻿namespace Jsl.FxCop
{
    using System.Collections;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using System.Reflection;
    using Microsoft.FxCop.Sdk;

    /// <summary>
    /// A utility class for working with <see cref="AssemblyNode"/>s.
    /// </summary>
    public static class AssemblyNodeHelper
    {
        private static readonly Hashtable assemblyCache = new Hashtable();

        #region Public Methods
        /// <summary>
        /// Gets the assembly node.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns>The <see cref="AssemblyNode"/> for the given <paramref name="assembly"/>.</returns>
        public static AssemblyNode GetAssemblyNode(this Assembly assembly)
        {
            return AssemblyNodeHelper.GetAssemblyNode(assembly.Location);
        }

        /// <summary>
        /// Gets the assembly node.
        /// </summary>
        /// <param name="assemblyLocation">The assembly location.</param>
        /// <returns>The <see cref="AssemblyNode"/> for the given <paramref name="assembly"/>.</returns>
        public static AssemblyNode GetAssemblyNode(string assemblyLocation)
        {
            Contract.Requires(assemblyLocation != null);
            Contract.Ensures(Contract.Result<AssemblyNode>() != null);

            // Try to get the assembly node.
            var assemblyNode = AssemblyNode.GetAssembly(
                assemblyLocation,
                assemblyCache,
                doNotLockFile: false,
                getDebugInfo: true,
                useGlobalCache: false);
            if (assemblyNode == null)
            {
                throw new InvalidFilterCriteriaException("Couldn't get the AssemblyNode for the specified assembly.");
            }

            return assemblyNode;
        }

        public static bool IsDebugCompiled(this AssemblyNode assembly)
        {
            // DEBUG:
            // [assembly: Debuggable(DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.EnableEditAndContinue | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.Default)]
            // RELEASE:
            // [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]

            var debuggableAttribute =
                (from attribute in assembly.Attributes
                 where attribute.Type.FullName == "System.Diagnostics.DebuggableAttribute"
                 select attribute)
                .FirstOrDefault();

            // Assume Release mode if no attribute.
            if (debuggableAttribute == null)
            {
                return false;
            }

            var literal = debuggableAttribute.Expressions[0] as Literal;
            var value = (DebuggableAttribute.DebuggingModes)literal.Value;

            return value.HasFlag(DebuggableAttribute.DebuggingModes.DisableOptimizations);
        }
        #endregion Public Methods
    }
}